﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Data;
using System.Data.SqlClient;
using AForge.Controls;
using AForge.Video;
using AForge.Video.DirectShow;

namespace AutoParkingManagement
{
    public partial class LaneExit : UserControl
    {
        #region Private members
        private EntryExit _entryExit;
        private bool _isLeft;
        private bool _saved=true;
        private string _message = String.Empty;
        private string _urlFrontCamera = String.Empty;
        private string _urlRearCamera = String.Empty;
        private bool _wrongPlateNumber = false;
        private bool _isPrepaid = false;
        private bool _isVIP = false;
        private string _plateNo = String.Empty;
        private bool _isMonthCard = false;
        private bool _isMonthExpired = false;

        private int RFID
        {
            get
            {
                if (_isLeft)
                {
                    return AppConfig.RFIDLEFT;
                }
                else
                {
                    return AppConfig.RFIDRIGHT;
                }
            }
        }
        /// <summary>
        /// Use card reader and get CardID        /// 
        /// </summary>
        /// <returns></returns>
        private string GetCardID()
        {
            _entryExit.Message = String.Empty;
            if (AppConfig.HasCardReader == "0") //only for demo
            {
                StringBuilder sb = new StringBuilder();
                sb.Append("SELECT TOP 1 CardID FROM Card ");
                object obj = SqlHelper.ExecuteScalar(CommandType.Text, sb.ToString(), null);
                if (obj == null) return Guid.Empty.ToString();
                return obj.ToString();
            }
            else //Read card to get card id
            {
                string strCardID = String.Empty;
                string strMessage = String.Empty;
                if (_isLeft)
                {
                    if (!DLL1356.GetCardIDByCardReader(AppConfig.RFIDLEFT, ref strCardID, ref strMessage))
                    {
                        strCardID = String.Empty;
                        _entryExit.Message = strMessage;
                    }
                    return strCardID;
                }
                else
                {
                    if (!DLL1356.GetCardIDByCardReader(AppConfig.RFIDRIGHT, ref strCardID, ref strMessage))
                    {
                        strCardID = String.Empty;
                        _entryExit.Message = strMessage;
                    }
                    return strCardID;
                }
            }
            //return Guid.NewGuid().ToString();
        }
        /// <summary>
        /// Get entry data by cardid. Entry data: entry time, front image, rear image
        /// </summary>
        /// <param name="cardID"></param>
        private bool GetEntryInfo(string cardID)
        {
            _entryExit.Card = new Card();
            _entryExit.Card.Id = cardID;
            /////////////
            StringBuilder sb = new StringBuilder();
            sb.Append("SELECT TOP 1 PlateNumber, FrontImageEntry, RearImageEntry, EntryTime, c.CardTypeID, [Name] ");
            sb.Append("FROM EntryExit e INNER JOIN [Card] c ON e.CardID = c.CardID ");
            sb.Append("INNER JOIN CardType t ON c.CardTypeID = t.CardTypeID ");
            sb.Append("WHERE e.CardID = @CardID AND ExitTime IS NULL ");
            sb.Append("ORDER BY EntryTime DESC");
            SqlParameter parm = new SqlParameter("@CardID", SqlDbType.NVarChar, 50);
            parm.Value = cardID;

            SqlDataReader objEntry = SqlHelper.ExecuteReader(CommandType.Text, sb.ToString(), parm);
            if (!objEntry.HasRows) return false;
            while (objEntry.Read())
            {
                _entryExit.EntryPlateNumber=objEntry.GetString(0);
                _entryExit.FrontImageEntry= (byte[])objEntry[1];
                _entryExit.RearImageEntry=(byte[])objEntry[2];
                _entryExit.EntryTime=objEntry.GetDateTime(3);
                _entryExit.Card.CardType = new CardType();
                _entryExit.Card.CardType.Id = objEntry.GetGuid(4);
                _entryExit.Card.CardType.Name = objEntry.GetString(5);                                       
            }
            return true;
        }

        /// <summary>
        /// Check time is night/day
        /// </summary>
        /// <param name="time">time</param>
        /// <returns>true: night; false: day</returns>
        private bool IsNight(DateTime time)
        {
            DateTime tmp = new DateTime(1900, 01, 01, time.Hour, time.Minute, 0);
            StringBuilder sb = new StringBuilder();
            sb.Append("SELECT DayNightID, FromTime, ToTime FROM DayNight WHERE DayNightID =1");
            SqlDataReader obj = SqlHelper.ExecuteReader(CommandType.Text, sb.ToString(), null);

            obj.Read();
            DateTime fromTime = obj.GetDateTime(1);
            DateTime toTime = obj.GetDateTime(2);
            if (fromTime > toTime) //19:30~01:10
            {
                fromTime = toTime.AddMinutes(1);
                toTime = toTime.AddHours(24);
            }
            return (fromTime <= time && time <= toTime);
        }

        /// <summary>
        /// Check cardid is prepaid or not
        /// </summary>
        /// <param name="cardID">card id</param>
        /// <returns>true/false</returns>
        private bool IsPrepaid(string cardID)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("SELECT Count(CardID) FROM Card ");
            sb.Append("WHERE CardID = @CardID ");
            sb.Append("AND (GetDate() BETWEEN FromDate AND ToDate)");
            SqlParameter parm = new SqlParameter("@CardID", SqlDbType.NVarChar, 50);
            parm.Value = cardID;
            object obj = SqlHelper.ExecuteScalar(CommandType.Text, sb.ToString(), parm);
            return (int)obj>0;
        }

        private bool IsVIP(string cardID)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("SELECT Count(CardID) FROM Card ");
            sb.Append("WHERE CardID = @CardID ");
            sb.Append("AND VIP=1");
            SqlParameter parm = new SqlParameter("@CardID", SqlDbType.NVarChar, 50);
            parm.Value = cardID;
            object obj = SqlHelper.ExecuteScalar(CommandType.Text, sb.ToString(), parm);
            return (int)obj > 0;
        }

        /// <summary>
        /// Check if card will be kept by customer
        /// </summary>
        /// <param name="cardID"></param>
        /// <returns></returns>
        private bool CardBelongsToCustomer(string cardID)
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("SELECT Count(CardID) FROM Card ");
            sb.Append("WHERE (CardID = @CardID) ");
            sb.Append("AND (FromDate IS NOT NULL) AND (ToDate IS NOT NULL)");
            SqlParameter parm = new SqlParameter("@CardID", SqlDbType.NVarChar, 50);
            parm.Value = cardID;
            object obj = SqlHelper.ExecuteScalar(CommandType.Text, sb.ToString(), parm);
            return (int)obj > 0;
        }

        /// <summary>
        /// Get price by card id
        /// </summary>
        /// <param name="cardID">card id</param>
        /// <returns>price</returns>
        private long GetPrice(string cardID)
        {
            _isVIP = IsVIP(cardID);
            //_isPrepaid = IsPrepaid(cardID);
            //if (_isVIP||_isPrepaid) return 0;
            if (_isVIP) return 0;
            StringBuilder sb = new StringBuilder();
            sb.Append("SELECT Price FROM CardPrice ");
            //sb.Append("WHERE DayNightID=@DayNightID ");
            sb.Append("WHERE CardTypeID=(SELECT CardTypeID FROM Card WHERE CardID =@CardID)");
            //SqlParameter[] parms = new SqlParameter[2];
            //parms[0] = new SqlParameter("DayNightID", SqlDbType.Bit);

            SqlParameter parm = new SqlParameter("CardID", SqlDbType.NVarChar, 50);
            parm.Value = _entryExit.Card.Id;            

            DateTime entryTime = _entryExit.EntryTime;
            DateTime exitTime = Common.GetSystemDBTime();
            TimeSpan timeDiff = exitTime.Subtract(entryTime);
            int hours = timeDiff.Days * 24 + timeDiff.Hours;
            if (hours == 0) hours = 1;//because minimum is 1            
            int days = (int)Math.Ceiling(1.0D * hours / 24);
            object obj = SqlHelper.ExecuteScalar(CommandType.Text, sb.ToString(), parm);
            return days*(long)obj;
        }

        /// <summary>
        /// Validate the current card can exit or not
        /// </summary>
        /// <returns></returns>
        private bool CanExit()
        {         
            ///////////////////////////////////////
            StringBuilder sb = new StringBuilder();
            sb.Append("SELECT Count(ID) FROM EntryExit ");
            sb.Append("WHERE CardID = @CardID AND dbo.ufn_StripCharacters(PlateNumber, '^a-z0-9')=dbo.ufn_StripCharacters(@PlateNumber, '^a-z0-9') ");
            sb.Append("AND ExitTime IS NULL");

            SqlParameter[] parms = new SqlParameter[2];
            parms[0] = new SqlParameter("CardID", SqlDbType.NVarChar, 50);            
            parms[0].Value = _entryExit.Card.Id;

            parms[1] = new SqlParameter("PlateNumber", SqlDbType.NVarChar, 50);
            parms[1].Value = _entryExit.PlateNumber;
            object objEntry = SqlHelper.ExecuteScalar(CommandType.Text, sb.ToString(), parms);
            return ((int)objEntry > 0);
        }        

        /// <summary>
        /// Reset images
        /// </summary>
        private void ResetImages()
        {
            picFrontOld.Appearance.ImageBackground = Properties.Resources.NoImage;
            picRearOld.Appearance.ImageBackground = Properties.Resources.NoImage;            
            _saved = true;
            if (_entryExit != null)
            {
                _entryExit.Card = null;
                _entryExit.RearImageEntry = null;
                _entryExit.RearImageExit = null;
                GC.Collect();
            }
        }

        /// <summary>
        /// Read rear image and return plate number
        /// </summary>
        /// <returns></returns>
        private string GetPlateNumber(string imagePath)
        {
            if (AppConfig.HasImageToText == "0")
            {
                //return String.Format("{0}", DateTime.Now.ToString("ssB-ss.mm.ss"));
                return String.Empty;
            }
            string strPlateNumber = String.Empty;
            try
            {
                int status;
                int iID_Cam = 1;
                bool Plate_Flag = false;
                String Image = imagePath;
                int iCorrect = 0;
                int iTime = 0;
                String Time = String.Empty;
                String Plate_number = String.Empty;
                String Vehicle = String.Empty;
                status = DLL1356.get_image(iID_Cam, ref Plate_Flag, ref Image, ref iCorrect, ref iTime,
                                  ref Time, ref Plate_number, ref Vehicle);
                if (status == 950) return Plate_number;                
            }
            catch (Exception ex)
            {
                //MessageBox.Show(ex.ToString());
                throw ex;
            }
            return strPlateNumber;
        }

        #endregion

        #region Properties
        public bool IsLeft
        {
            get
            {
                return _isLeft;
            }
            set
            {
                _isLeft = value;
            }
        }
        public string URLFrontCamera
        {
            get
            {
                return _urlFrontCamera;
            }
            set
            {
                _urlFrontCamera = value;
            }
        }

        public string URLRearCamera
        {
            get
            {
                return _urlRearCamera;
            }
            set
            {
                _urlRearCamera = value;
            }
        }
        public EntryExit EntryExit
        {
            get
            {
                return _entryExit;
            }
            set
            {
                _entryExit = value;
            }
        }

        public bool CameraVisible
        {
            get
            {
                return webCamFront.Visible;
            }
        }

        public bool Saved
        {
            get
            {
                return _saved;
            }
            set
            {
                _saved = value;
            }
        }

        public string Message
        {
            get
            {
                return _message;
            }
            set
            {
                _message = value;
            }
        }

        public bool WrongPlateNumber
        {
            get
            {
                return _wrongPlateNumber;
            }
        }
        #endregion

        #region Public members
        public void StartCameras()
        {
            string strNumberDat = String.Format(@"{0}\{1}", Application.StartupPath, "DataFile0to9.dat");
            string strCharDat = String.Format(@"{0}\{1}", Application.StartupPath, "DataFileAtoZ.dat");
            string[] databasePaths = new string[2] { strNumberDat, strCharDat };        
            // create video source
            if (AppConfig.HasIPCamera == "0")//only for demo without ip camera
            {
                FileVideoSource fileSourceFront = new FileVideoSource(AppConfig.FrontVideoFile);
                IPCameraHelper.OpenVideoSource(webCamFront, fileSourceFront);
                FileVideoSource fileSourceRear = new FileVideoSource(AppConfig.RearVideoFile);
                IPCameraHelper.OpenVideoSource(webCamRear, fileSourceRear);
            }
            else
            {
                MJPEGStream mjpegSourceFront = new MJPEGStream(_urlFrontCamera);
                MJPEGStream mjpegSourceRear = new MJPEGStream(_urlRearCamera);
                IPCameraHelper.OpenVideoSource(webCamFront, mjpegSourceFront);
                IPCameraHelper.OpenVideoSource(webCamRear, mjpegSourceRear);
            }            
        }

        /// <summary>
        /// Capture plate Number
        /// </summary>
        /// <returns>plate number</returns>
        public string CapturePlateNumber()
        {
            try
            {
                _message = String.Empty;
                if (_saved) //camera is showing
                {
                    Image frontNewImage = null;
                    Image rearNewImage = null;
                    frontNewImage = IPCameraHelper.CaptureImage(webCamFront);
                    string strSide = "phải";
                    if (_isLeft) strSide = "trái";
                    if (frontNewImage == null)
                    {
                        _message = String.Format("Vui lòng kiểm tra lại camera phía trước (bên {0})", strSide);
                        return String.Empty;
                    }

                    rearNewImage = IPCameraHelper.CaptureImage(webCamRear);
                    if (rearNewImage == null)
                    {
                        _message = String.Format("Vui lòng kiểm tra lại camera phía sau (bên {0})", strSide);
                        return String.Empty;
                    }
                    frontNewImage = Common.ResizeImage(frontNewImage, webCamFront.Size.Width, webCamFront.Size.Height);
                    rearNewImage = Common.ResizeImage(rearNewImage, webCamRear.Size.Width, webCamRear.Size.Height);
                    _entryExit.FrontImageExit = Common.ImageToByteArray(frontNewImage);
                    _entryExit.RearImageExit = Common.ImageToByteArray(rearNewImage);
                    //string strRearImagePath = Common.SaveImage(rearNewImage);
                    //Read image and return plate number to display in label plate number
                    //_entryExit.PlateNumber = GetPlateNumber(strRearImagePath);
                    _entryExit.PlateNumber = _plateNo;
                    ////////////////////////////////////////
                    string cardID = GetCardID();
                    if (!String.IsNullOrEmpty(_entryExit.Message))
                    {
                        //MessageBox.Show(_entryExit.Message);
                        return String.Empty;
                    }
                    ///////////////////////
                    if (String.IsNullOrEmpty(cardID))
                    {
                        //MessageBox.Show("Vui lòng đưa thẻ vào và chụp lại hình.");
                        _message = "Vui lòng đưa thẻ vào và chụp lại hình.";
                        return String.Empty;
                    }
                    ////////////////////////////////////////
                    if (!GetEntryInfo(cardID))
                    {
                        _message = "Thẻ này chưa vào cổng.\nVui lòng thu lại thẻ để kiểm tra.";
                        return String.Empty;
                    }
                    picFrontOld.Appearance.ImageBackground = Common.ByteArrayToImage(_entryExit.FrontImageEntry);
                    picRearOld.Appearance.ImageBackground = Common.ByteArrayToImage(_entryExit.RearImageEntry);
                    ////////////////////////////////////////
                    //Stop cameras                     
                    webCamFront.VideoSource.Stop();                    
                    webCamRear.VideoSource.Stop();
                    _saved = false;
                    return _entryExit.PlateNumber;            
                }
                /////////////////////////////////
                webCamFront.VideoSource.Start();
                webCamRear.VideoSource.Start();
                //Clear old images
                picFrontOld.Appearance.ImageBackground = Properties.Resources.NoImage;
                picRearOld.Appearance.ImageBackground = Properties.Resources.NoImage;
                _saved = true;
                return String.Empty;             
                
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private string GetParkingFloorById(Guid id)
        {
            string sql = @"SELECT [Name]      
                  FROM [ParkingFloor]
                  WHERE ParkingFloorID=@ParkingFloorID";
            SqlParameter parm = new SqlParameter("@ParkingFloorID", SqlDbType.UniqueIdentifier);
            parm.Value = id;
            object obj = SqlHelper.ExecuteScalar(CommandType.Text, sql, parm);
            if (obj == null) return String.Empty;
            return obj.ToString();
        }

        /// <summary>
        /// Save plate number
        /// </summary>
        /// <returns></returns>
        public bool SavePlateNumber()
        {
            _wrongPlateNumber = false;
            _message = String.Empty;
            if (!_saved)
            {
                StringBuilder sb = new StringBuilder();
                if (GetCardID() != _entryExit.Card.Id) //Save.CardID !=Capture.CardID
                {                    
                    sb.Append("Thẻ lúc chụp hình và thẻ lúc kiểm tra khác nhau.");
                    sb.Append("\nVui lòng kiểm tra lại.");
                    _message = sb.ToString();
                    _wrongPlateNumber = true;                    
                    return false;
                } 

                if (!CanExit())
                {                                       
                    sb.Append("\nBiển số xe vào: {0}");
                    sb.Append("\nBiển số xe ra: {1}");
                    sb.Append("\nBạn có đồng ý cho xe ra hay không?");
                    _message = String.Format(sb.ToString(), _entryExit.EntryPlateNumber, _entryExit.PlateNumber);
                    YesNoMessage frm = new YesNoMessage(this.FindForm().Text, _message);
                    frm.ShowDialog(this.FindForm());
                    if (this.FindForm().Tag.ToString() == DialogResult.No.ToString())
                    {
                        _message = String.Empty;
                        _wrongPlateNumber = true;                        
                        return false;
                    }
                }
                GC.Collect();
                /////////////////////////
                //Save data to card
                if (AppConfig.HasCardReader == "1" && !DLL1356.WritePlate(RFID, _entryExit.PlateNumber, ref _message))
                {
                    _entryExit.Message = _message;
                    return false;
                }
                /////////////////////////
                sb = new StringBuilder();
                sb.Append("UPDATE EntryExit ");
                sb.Append("SET FrontImageExit=@FrontImageExit, ");
                sb.Append("RearImageExit=@RearImageExit, ");
                sb.Append("PlateNumber = @PlateNumber, ");
                sb.Append("ExitTime = GetDate(), ");
                sb.Append("Price= @Price, ");
                sb.Append("EntryPlate= @EntryPlate, ");
                sb.Append("ExitUpdateUser=@ExitUpdateUser, ");
                sb.Append("IsMonthCard=@IsMonthCard ");
                sb.Append("WHERE CardID = @CardID AND ExitTime IS NULL ");                
                SqlParameter[] parms = new SqlParameter[8];
                parms[0] = new SqlParameter("@FrontImageExit", SqlDbType.Image);                
                parms[0].Value = Common.DBNullImage(_entryExit.FrontImageExit);

                parms[1] = new SqlParameter("@RearImageExit", SqlDbType.Image);
                parms[1].Value = Common.DBNullImage(_entryExit.RearImageExit);

                parms[2] = new SqlParameter("@PlateNumber", SqlDbType.NVarChar,50);
                parms[2].Value = _entryExit.PlateNumber;

                parms[3] = new SqlParameter("@Price", SqlDbType.Decimal);
                MonthCard monthCard = new MonthCard();
                monthCard = monthCard.GetByPlateNumber(_entryExit.PlateNumber);
                string parkingFloor = String.Empty;
                bool isWrongFloor = false;
                if (monthCard != null)
                {
                    parkingFloor = GetParkingFloorById(monthCard.ParkingFloorId);
                    if(parkingFloor.ToUpper().Equals(AppConfig.ParkingFloor.ToUpper()))
                    {
                        _isMonthCard = true;
                        _isMonthExpired = monthCard.IsExpired;
                        _entryExit.Price = _entryExit.Price;
                    }
                    else
                    {
                        isWrongFloor = true;
                        _isMonthCard = false;
                        _entryExit.Price = GetPrice(_entryExit.Card.Id);   
                    }                    
                }
                else
                {
                    _isMonthCard = false;
                    _entryExit.Price = GetPrice(_entryExit.Card.Id);   
                }
                parms[3].Value = _entryExit.Price;
                parms[4] = new SqlParameter("@ExitUpdateUser", SqlDbType.UniqueIdentifier);
                parms[4].Value = MainParent.EmpWorking.Employee.Id;

                parms[5] = new SqlParameter("@EntryPlate", SqlDbType.NVarChar, 50);
                parms[5].Value = _entryExit.EntryPlateNumber;

                parms[6] = new SqlParameter("@CardID", SqlDbType.NVarChar, 50);
                parms[6].Value = _entryExit.Card.Id;

                parms[7] = new SqlParameter("@IsMonthCard", SqlDbType.Bit);
                parms[7].Value = _isMonthCard;  
                SqlHelper.ExecuteNonQuery(CommandType.Text, sb.ToString(), parms);                
                _saved = true;
                if (_isMonthCard)
                {
                    _message = "Thẻ tháng.\nGhi thẻ xong.\nVui lòng thu lại thẻ từ khách hàng.";
                    if (_isMonthExpired)
                    {
                        _message = "Thẻ tháng đã hết hạn.\nGhi thẻ xong.\nVui lòng thu lại thẻ từ khách hàng.";
                        _message += "\nNhắc nhở khách hàng gia hạn thẻ.";
                    }
                }
                else
                {
                    if (CardBelongsToCustomer(_entryExit.Card.Id) || _isVIP)
                    {
                        _message = "Ghi thẻ xong.\nVui lòng trả lại thẻ cho khách hàng.";
                    }
                    else
                    {
                        _message = "Ghi thẻ xong.\nVui lòng thu lại thẻ từ khách hàng.";
                    }

                    if (_entryExit.Price > 0)
                    {
                        _message += String.Format("\nvà thu tiền {0} đồng", _entryExit.Price.ToString("###,###"));
                        if (isWrongFloor)
                        {
                            _message += String.Format("\n(Vé tháng gởi sai hầm {0})", parkingFloor);
                        }
                    }
                    else
                    {
                        if (_isVIP)
                        {
                            _message += "\n(Loại thẻ VIP)";
                        }
                        if (_isPrepaid)
                        {
                            _message += "\n(Loại thẻ trả trước)";
                        }
                    }
                }    
            }
            ResetImages();
            StartCameras();
            return _saved;
        }

        public void ResetLane()
        {
            ResetImages();
            _message = String.Empty;
            _saved = true;
        }

        /// <summary>
        /// Stop cameras
        /// </summary>
        public void StopCameras()
        {
            try
            {
                IPCameraHelper.CloseVideoSource(webCamFront);
                IPCameraHelper.CloseVideoSource(webCamRear);
            }
            catch (Exception ex)
            {

                throw ex;
            }
        }
        #endregion

        #region Events
        private void LaneExit_Load(object sender, EventArgs e)
        {
            ResetImages();
            if (!_isLeft)
            {
                pnlLeft.Dock = DockStyle.Right;
                pnlRight.Dock = DockStyle.Left;
            }
        }

        //private void webCamRear_GetImageInfo(object sender, IPACam.InfoEventArgs e)
        //{
        //    try
        //    {
        //        if (e.Plate_Number_Flag && e.Plate.Count > 0 && !String.IsNullOrEmpty(e.Bienso))
        //        {
        //            _plateNo = e.Bienso;
        //        }
        //    }
        //    catch
        //    {
        //    }    
        //}
        #endregion

        #region Constructors
        public LaneExit(bool isLeft)
        {
            InitializeComponent();
            CheckForIllegalCrossThreadCalls = false;
            _isLeft = isLeft;

        }
        #endregion

        
        
    }
}
